package com.gee.spring.cloud.feign.flow.execute;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gee.spring.cloud.feign.flow.binding.annotation.FlowArg;
import com.gee.spring.cloud.feign.flow.exception.ArgNotFoundException;
import com.gee.spring.cloud.feign.flow.result.ExecuteState;
import com.gee.spring.cloud.feign.flow.result.WorkResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * desc:
 *  总的执行结果
 * @author gee wrote on  2020-08-30 08:20:14
 */

public class ExecuteContext implements Serializable {

    public static final String ARGS_SEPARATOR = "\\.";

    public static final String PARAM_PREFIX = "#";

    public static final String TEMP_VAR_PREFIX = "*";

    public static final String WORK_RESULT_PREFIX = "$";

    private static final long serialVersionUID = -1327492165034638074L;

    private static final Logger logger = LoggerFactory.getLogger(ExecuteContext.class);
    /**
     * 总的执行结果
     * {@link ExecuteState}
     */
    private AtomicInteger globalExecuteState = new AtomicInteger(0);

    private transient Exception globalException;

    private String executeMessage;

    private Map<String, Object> paramMap;

    private Map<String,Object> tempVarMap = new HashMap<>();

    private transient Executor executor;

    private String preFUSWorkId;


    private Map<String, WorkResult<?>> workResultMap = new HashMap<>();

    public Map<String, WorkResult<?>> getWorkResultMap() {
        return workResultMap;
    }

    public ExecuteContext(Map<String, Object> paramMap) {
        this.paramMap = paramMap;
    }

    public AtomicInteger getGlobalExecuteState() {
        return globalExecuteState;
    }

    public Exception getGlobalException() {
        return globalException;
    }

    public void setGlobalException(Exception globalException) {
        this.globalException = globalException;
    }

    public Executor getExecutor() {
        return executor;
    }

    public void setExecutor(Executor executor) {
        this.executor = executor;
    }

    public Map<String, Object> getTempVarMap() {
        return tempVarMap;
    }

    @Override
    public String toString() {
        return "ExecuteContext{" +
                "globalExecuteState=" + globalExecuteState +
                ", globalException=" + globalException +
                ", paramMap=" + paramMap +
                ", preFUSWorkId=" + preFUSWorkId +
                ", workResultMap=" + workResultMap +
                '}';
    }
    public WorkResult<?> getWorkResult(String id){
        return getWorkResultMap().get(id);
    }

    public <R> R getResult(String workId, Class<R> resultType){
        return getWorkResult(workId).getResult(resultType);
    }

    public String getPreFUSWorkId() {
        return preFUSWorkId;
    }

    public void setPreFUSWorkId(String preFUSWorkId) {
        this.preFUSWorkId = preFUSWorkId;
    }

    public String getExecuteMessage() {
        return executeMessage;
    }

    public void setExecuteMessage(String executeMessage) {
        this.executeMessage = executeMessage;
    }

    public AtomicInteger getWorkExecuteState(String workId){
        return getWorkResult(workId).getExecuteState();
    }

    public static class Builder{

        public static  ExecuteContext build(Map<String, Object> paramMap, Executor executor){
            ExecuteContext executeContext = new ExecuteContext(paramMap);
            executeContext.setExecutor(executor);
            return executeContext;
        }
    }


    public boolean isSuccessFul(){
        return globalExecuteState.get() == ExecuteState.SUCCESSFUL.getCode();
    }

    public boolean isExceptional(){
        return globalExecuteState.get() == ExecuteState.EXCEPTIONAL.getCode();
    }

    public Object[] getArgs(FlowArg[] argAnnotations) throws ArgNotFoundException {
        if (argAnnotations == null || argAnnotations.length == 0){
            return null;
        }
        Object[] args = new Object[argAnnotations.length];
        for (int i = 0; i < argAnnotations.length; i++) {
            args[i] = getArg(argAnnotations[i]);
        }
        return args;
    }
    public Object getArg(FlowArg argAnnotation) throws ArgNotFoundException {
        if (argAnnotation == null || argAnnotation.key().length() == 0){
            return null;
        }
        String key = argAnnotation.key();
        Class<?> argType = argAnnotation.argType();
        JSONObject request = null;
        String path= null;
        if (key.startsWith(PARAM_PREFIX)){
            request = JSONUtil.parseObj(this.paramMap);
            path = key.substring(1);
        } else if (key.startsWith(TEMP_VAR_PREFIX)){
            request = JSONUtil.parseObj(this.tempVarMap);
            path = key.substring(1);
        }else if (key.startsWith(WORK_RESULT_PREFIX)){

            String workId = key.split(ARGS_SEPARATOR)[0].substring(1);
            WorkResult<?> workResult = getWorkResult(workId);
            if (workResult == null){
                throw  new ArgNotFoundException("work result not found with the key: " + key);
            }
            if (!key.contains(".")){
                return workResult;
            }
            request = JSONUtil.parseObj(workResult);

            path = key.substring(key.indexOf(".") + 1);
        }else {
            logger.error("arg key: {} is error should start with # for param,  * for temp variable, $ for work result",key);
            throw new ArgNotFoundException("arg key should start with # for param,  * for temp variable, $ for work result");
        }
        return request.getByPath(path, argType);
    }
}
